package create_module

import (
	"fmt"
	"gitee.com/zjlsliupei/ghelp"
	"gitee.com/zjlsliupei/gweb-cli/utils"
	"github.com/beego/beego/v2/core/logs"
	"github.com/dave/jennifer/jen"
	"github.com/tidwall/gjson"
	"os"
	"path/filepath"
	"strings"
)

type CreateModule struct {
	confContent string
	out         string
	tableName   string
	// model输出文件
	outModel string
	// controller输出文件
	outController string
	// router输出文件
	outRouter string
}

func BatchCreateModule(conf string, out string) {
	var confs []string
	if ghelp.IsDir(conf) {
		_confs, err := utils.GetConfPathByDir(conf, ".json")
		if err != nil {
			return
		}
		confs = _confs
	} else {
		confs = append(confs, conf)
	}
	for _, v := range confs {
		m, err := NewCreateModule(v, out)
		if err != nil {
			logs.Error("Init CreateModule err", err)
			os.Exit(1)
		}
		err = m.CreateRouter()
		if err != nil {
			logs.Error("Create Router err", err)
			os.Exit(1)
		}
		m.CreateController()
		if err != nil {
			logs.Error("Create Controller err", err)
			os.Exit(1)
		}
		m.CreateModel()
		if err != nil {
			logs.Error("Create Model err", err)
			os.Exit(1)
		}
	}

}

// NewCreateModule 初始化模块命令
// conf 配置文件内容
// out 输出根目录
func NewCreateModule(conf string, out string) (CreateModule, error) {
	content, err := os.ReadFile(conf)
	if err != nil {
		return CreateModule{}, err
	}
	confContent := string(content)
	return CreateModule{
		confContent:   confContent,
		out:           out,
		outModel:      fmt.Sprintf("%s/%s/%s%s", out, "models", gjson.Get(confContent, "tableName").String(), ".go"),
		outController: fmt.Sprintf("%s/%s/%s%s", out, "controllers", gjson.Get(confContent, "tableName").String(), ".go"),
		outRouter:     fmt.Sprintf("%s/%s/%s%s", out, "routers", gjson.Get(confContent, "tableName").String(), ".go"),
		tableName:     gjson.Get(confContent, "tableName").String(),
	}, nil
}

// 控制器模板
var controllerTmpl = `
package controllers

import (
	"gitee.com/zjlsliupei/ghelp"
	"gitee.com/zjlsliupei/gweb"
	"github.com/beego/beego/v2/core/logs"
	"github.com/gin-gonic/gin"
	"github.com/gookit/validate"
	"skeleton/models"
	"skeleton/utils"
)


func {{big_table_name}}Add(c *gin.Context) {
	// 参数获取
	{{small_table_name}}Model := models.{{big_table_name}}{}
	err := c.ShouldBind(&{{small_table_name}}Model)
	if err != nil {
		gweb.Json(c, gweb.JsonFailCode, err.Error(), nil)
		return
	}
	// 参数验证
	v := validate.Struct(&{{small_table_name}}Model).WithScenarios({{small_table_name}}Model.GetScene())
	if !v.Validate("add") {
		gweb.Json(c, gweb.JsonFailCode, v.Errors.One(), nil)
		return
	}
	// 数据库操作
	res := gweb.GetDB().Create(&{{small_table_name}}Model)
	if res.Error != nil {
		logs.Error("{{big_table_name}}Add err:", res.Error.Error())
		gweb.Json(c, gweb.JsonFailCode, "新增失败", nil)
		return
	}
	// todo : 其它业务处理
	gweb.Json(c, gweb.JsonSuccessCode, "success", ghelp.MAP{
		"id": demoModel.ID,
	})
}

func {{big_table_name}}Update(c *gin.Context) {
	// 参数获取
	{{small_table_name}}Model := models.{{big_table_name}}{}
	err := c.ShouldBind(&{{small_table_name}}Model)
	if err != nil {
		gweb.Json(c, gweb.JsonFailCode, err.Error(), nil)
		return
	}
	// 参数验证
	v := validate.Struct(&{{small_table_name}}Model).WithScenarios({{small_table_name}}Model.GetScene())
	if !v.Validate("update") {
		gweb.Json(c, gweb.JsonFailCode, v.Errors.One(), nil)
		return
	}
	// 数据库操作
	res := gweb.GetDB().Omit("created_at", "deleted_at").Save(&{{small_table_name}}Model)
	if res.Error != nil {
		logs.Error("{{big_table_name}}Add err:", res.Error.Error())
		gweb.Json(c, gweb.JsonFailCode, "修改失败", nil)
		return
	}
	// todo : 其它业务处理
	gweb.Json(c, gweb.JsonSuccessCode, "success", nil)
}

func {{big_table_name}}Del(c *gin.Context) {
	// 参数获取
	{{small_table_name}}Model := models.{{big_table_name}}{}
	err := c.ShouldBind(&{{small_table_name}}Model)
	if err != nil {
		gweb.Json(c, gweb.JsonFailCode, err.Error(), nil)
		return
	}
	// 参数验证
	v := validate.Struct(&{{small_table_name}}Model).WithScenarios({{small_table_name}}Model.GetScene())
	if !v.Validate("del") {
		gweb.Json(c, gweb.JsonFailCode, v.Errors.One(), nil)
		return
	}
	// 数据库操作
	res := gweb.GetDB().Delete(&{{small_table_name}}Model)
	if res.Error != nil {
		logs.Error("{{small_table_name}}Del err:", res.Error.Error())
		gweb.Json(c, gweb.JsonFailCode, "删除失败", nil)
		return
	}
	// todo : 其它业务处理
	gweb.Json(c, gweb.JsonSuccessCode, "success", nil)
}

func {{big_table_name}}Get(c *gin.Context) {
	// 参数获取
	{{small_table_name}}Model := models.{{big_table_name}}{}
	err := c.ShouldBind(&demoModel)
	if err != nil {
		gweb.Json(c, gweb.JsonFailCode, err.Error(), nil)
		return
	}
	// 参数验证
	v := validate.Struct(&{{small_table_name}}Model).WithScenarios({{small_table_name}}Model.GetScene())
	if !v.Validate("get") {
		gweb.Json(c, gweb.JsonFailCode, v.Errors.One(), nil)
		return
	}
	// 数据库操作
	res := gweb.GetDB().Find(&{{small_table_name}}Model)
	if res.Error != nil{
		logs.Error("{{small_table_name}}Get err:", res.Error.Error())
		gweb.Json(c, gweb.JsonFailCode, "获取失败", nil)
		return
	}
	if res.RowsAffected == 0 {
		logs.Error("{{small_table_name}}Get id:%d not found:", demoModel.ID)
		gweb.Json(c, gweb.JsonFailCode, "查无记录", nil)
		return
	}

	// todo : 其它业务处理
	gweb.Json(c, gweb.JsonSuccessCode, "success", ghelp.MAP{
		"info": {{small_table_name}}Model,
	})
}

func {{big_table_name}}List(c *gin.Context) {
	// 参数获取
	paramList := utils.ParamList{}
	err := c.ShouldBind(&paramList)
	if err != nil {
		gweb.Json(c, gweb.JsonFailCode, err.Error(), nil)
		return
	}
	// 参数验证
	v := validate.Struct(&paramList)
	if !v.Validate("get") {
		gweb.Json(c, gweb.JsonFailCode, v.Errors.One(), nil)
		return
	}
	// 数据库操作
	var total int64
	var list []models.{{big_table_name}}
	res := gweb.GetDB().
		Offset((paramList.Page-1) * paramList.PageSize).
		Limit(paramList.PageSize).
		Order("id desc").
		Find(&list).
		Offset(-1).
		Limit(-1).
		Count(&total)
	if res.Error != nil{
		logs.Error("{{small_table_name}}List err:", res.Error.Error())
		gweb.Json(c, gweb.JsonFailCode, "获取失败", nil)
		return
	}

	// todo : 其它业务处理
	gweb.Json(c, gweb.JsonSuccessCode, "success", ghelp.MAP{
		"list": list,
		"total": total,
	})
}
`

func (c *CreateModule) CreateController() error {
	err := utils.CreateDir(filepath.Dir(c.outController))
	if err != nil {
		return err
	}
	str := utils.ReplaceVar(controllerTmpl, map[string]string{
		"big_table_name":   utils.StrFirstToUpper(c.tableName),
		"small_table_name": c.tableName,
	})
	err = utils.CreateFile(c.outController, str, false)
	return err
}

// 路由模板
var routerTmpl = `
package routers

import (
	"gitee.com/zjlsliupei/gweb"
	"github.com/gin-gonic/gin"
	"skeleton/controllers"
)

func init() {
	gweb.RegistryRoute(func(app *gin.RouterGroup) {
		task := app.Group("/{{small_table_name}}")
		{
			task.POST("/add", controllers.{{big_table_name}}Add)
			task.POST("/update", controllers.{{big_table_name}}Update)
			task.POST("/del", controllers.{{big_table_name}}Del)
			task.POST("/get", controllers.{{big_table_name}}Get)
			task.POST("/list", controllers.{{big_table_name}}List)
		}
	})
}

`

func (c *CreateModule) CreateRouter() error {
	err := utils.CreateDir(filepath.Dir(c.outRouter))
	if err != nil {
		return err
	}
	str := utils.ReplaceVar(routerTmpl, map[string]string{
		"big_table_name":   utils.StrFirstToUpper(c.tableName),
		"small_table_name": c.tableName,
	})
	err = utils.CreateFile(c.outRouter, str, false)
	return err
}

func (c *CreateModule) CreateModel() error {
	err := utils.CreateDir(filepath.Dir(c.outModel))
	if err != nil {
		return err
	}
	fields := []ghelp.MAP{}
	fields = append(fields, ghelp.MAP{
		"name": "id",
		"type": "int",
		"tag":  `json:"id" gorm:"primarykey" form:"id" validate:"required|int"`,
	})
	fields = append(fields, ghelp.MAP{
		"name": "created_at",
		"type": "time.Time",
		"tag":  `json:"created_at"`,
	})
	fields = append(fields, ghelp.MAP{
		"name": "deleted_at",
		"type": "gorm.DeletedAt",
		"tag":  `json:"deleted_at"`,
	})
	fields = append(fields, ghelp.MAP{
		"name": "updated_at",
		"type": "time.Time",
		"tag":  `json:"updated_at"`,
	})
	// 解析配置文件
	gjson.Get(c.confContent, "columns").ForEach(func(key, value gjson.Result) bool {
		var tags []string
		if value.Get("validate").Exists() {
			tags = append(tags, fmt.Sprintf(`validate:"%s"`, value.Get("validate").String()))
		}
		if value.Get("gorm").Exists() {
			tags = append(tags, fmt.Sprintf(`gorm:"%s"`, value.Get("gorm").String()))
		}
		fields = append(fields, ghelp.MAP{
			"name": value.Get("name").String(),
			"type": value.Get("type").String(),
			"tag":  fmt.Sprintf("%s", strings.Join(tags, " ")),
		})
		return true
	})

	// 生成结构体
	m := jen.NewFile("models")
	head := m.Id("package models\n\nimport (\n\t\"github.com/gookit/validate\"\n\t\"gorm.io/gorm\"\n\t\"time\"\n)")
	var s []jen.Code
	for _, v := range fields {
		_v := jen.Id(utils.SnakeToCamelCaseForGorm(v["name"].(string))).Op(v["type"].(string))
		if tag, ok := v["tag"]; ok {
			ts := strings.Split(tag.(string), " ")
			var matTag = make(map[string]string)
			for _, t := range ts {
				_t := strings.Split(t, ":\"")
				if len(_t) == 2 {
					matTag[_t[0]] = strings.Trim(_t[1], "\"")
				}
			}
			if len(matTag) > 0 {
				_v.Tag(matTag)
			}
		}
		s = append(s, _v)
	}
	_c := m.Type().Id(utils.StrFirstToUpper(c.tableName)).Struct(s...)

	// 结构体增加验证方法
	var add []string
	var update []string
	var del []string
	var get []string
	for _, v := range fields {
		if v["name"] == "id" {
			del = append(del, fmt.Sprintf(`"%s"`, "ID"))
			update = append(update, fmt.Sprintf(`"%s"`, "ID"))
			get = append(get, fmt.Sprintf(`"%s"`, "ID"))
			continue
		}
		value := utils.SnakeToCamelCaseForGorm(v["name"].(string))
		add = append(add, fmt.Sprintf(`"%s"`, value))
		update = append(update, fmt.Sprintf(`"%s"`, value))
	}
	_f := m.Func().Params(jen.Id("this").Id("*" + utils.StrFirstToUpper(c.tableName))).
		Id("GetScene").Params().Id("validate.SValues").Block(
		jen.Return(jen.Id("validate.SValues").Block(
			jen.Id(`"add"`).Op(":").Id("[]string{").Id(strings.Join(add, ",")).Id("},"),
			jen.Id(`"update"`).Op(":").Id("[]string{").Id(strings.Join(update, ",")).Id("},"),
			jen.Id(`"del"`).Op(":").Id("[]string{").Id(strings.Join(del, ",")).Id("},"),
			jen.Id(`"get"`).Op(":").Id("[]string{").Id(strings.Join(get, ",")).Id("},"),
		)),
	)
	//logs.Info("%#v", _c)
	//logs.Info("%#v", _f)
	//logs.Info("%#v", head)
	err = utils.CreateFile(c.outModel, fmt.Sprintf("%#v\n\n%#v\n\n%#v", head, _c, _f), false)
	return err
}
